@file:Suppress("FunctionName")

package com.zxy.supplier_system.server.repository

import com.zxy.supplier_system.server.controller.MarketSaleDocumentController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.utils.SpecificationUtils
import com.zxy.supplier_system.server.utils.joinOnce
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
import org.springframework.data.jpa.repository.Query
import java.time.OffsetDateTime
import java.time.YearMonth

interface MarketSaleDocumentRepository : JpaRepository<MarketSaleDocument, Long>, JpaSpecificationExecutor<MarketSaleDocument> {

    fun findByIdAndAccount_Market_SystemService(id: Long, market_systemService: SystemService): MarketSaleDocument?

    fun findByAccount_IdAndYearAndMonthAndAccount_Market_SystemService(accountId: Int, year: Int, month: Int, market_systemService: SystemService): MarketSaleDocument?

    fun findAllByAccount_Market_Id(market_id: Int, pageable: Pageable): Slice<MarketSaleDocument>

    @Query("""
        SELECT DISTINCT d.year, d.month 
        FROM MarketSaleDocument d 
        WHERE d.account.market.id = ?1
    """)
    fun findDistinctYearMonthByMarket(marketId: Int): List<Array<Int>>

    fun findDistinctYearMonthObjectByMarket(marketId: Int): List<YearMonth>{
        return this.findDistinctYearMonthByMarket(marketId).map { YearMonth.of(it[0],it[1]) }
    }

    fun findAllByAccount_Market_IdAndAccount_Market_SystemServiceAndYearAndMonth(marketId: Int, systemService: SystemService,year: Int, month: Int): List<MarketSaleDocument>

    object Specifications {

        fun fromRequest(queryMarketSaleDocumentRequest: MarketSaleDocumentController.QueryMarketSaleDocumentRequest): Specification<MarketSaleDocument> {
            return Specification.allOf(
                marketIn(queryMarketSaleDocumentRequest.marketIds),
                // TODO year month
                employeeIn(queryMarketSaleDocumentRequest.createdByIds),
                statusEqual(queryMarketSaleDocumentRequest.status),
            )
        }

        fun ifUCanSee(employee: Employee): Specification<MarketSaleDocument> {
            if (employee.authorities.contains(Authority.卖场_退货单_查询所有)) {
                return Specification.where(null)
            }
            return marketSaleManagerEqual(employee.id!!)
                .or(marketShoppingGuidesContains(employee.id!!))
        }

        fun systemServiceEqual(systemService: SystemService): Specification<MarketSaleDocument> {
            return SystemServiceRepository.Specifications.systemServiceEquals(
                { root -> root.joinOnce<MarketSaleDocument,MarketAccount>("account").joinOnce("market") },
                systemService
            )
        }

        fun marketSaleManagerEqual(employeeId: Int): Specification<MarketSaleDocument> {
            return Specification { root, _, criteriaBuilder ->
                MarketRepository.Specifications.saleManagerEqual(root.joinOnce<MarketSaleDocument,MarketAccount>("account").joinOnce("market"), criteriaBuilder, employeeId)
            }
        }

        fun marketShoppingGuidesContains(employeeId: Int): Specification<MarketSaleDocument> {
            return Specification { root, query, criteriaBuilder ->
                MarketRepository.Specifications.shoppingGuidesContains(
                    root.joinOnce<MarketSaleDocument,MarketAccount>("account").joinOnce("market"),
                    criteriaBuilder,
                    query,
                    employeeId
                )
            }
        }

        fun marketIn(marketIds: List<Int>): Specification<MarketSaleDocument> {
            return SpecificationUtils.`in`({ root ->
                root.joinOnce<MarketSaleDocument,MarketAccount>("account").joinOnce<MarketAccount, Market>("market").get("id")
            }, marketIds)
        }

        fun employeeIn(employeeIds: List<Int>): Specification<MarketSaleDocument> {
            return SpecificationUtils.`in`({ root ->
                root.joinOnce<MarketSaleDocument, Employee>("employee").get("id")
            }, employeeIds)
        }

        fun statusEqual(status: MarketSaleDocument.Status?): Specification<MarketSaleDocument> {
            return Specification { root, query, criteriaBuilder ->
                if (status == null) {
                    null
                } else {
                    val submittedDateTimePath = root.get<OffsetDateTime>("submittedDateTime")
                    val auditedDateTimePath = root.get<OffsetDateTime>("auditedDateTime")
                    when (status) {
                        MarketSaleDocument.Status.WAIT_SUBMIT -> {
                            criteriaBuilder.isNull(submittedDateTimePath)
                        }

                        MarketSaleDocument.Status.SUBMITTED -> {
                            criteriaBuilder.and(
                                criteriaBuilder.isNotNull(submittedDateTimePath),
                                criteriaBuilder.isNull(
                                    auditedDateTimePath
                                )
                            )
                        }

                        else -> {
                            criteriaBuilder.and(
                                criteriaBuilder.isNotNull(
                                    auditedDateTimePath
                                )
                            )
                        }
                    }
                }
            }
        }

        fun idEqual(id: Long): Specification<MarketSaleDocument> {
            return Specification { root,
                                   _,
                                   criteriaBuilder ->
                criteriaBuilder.equal(root.get<Long>("id"), id)
            }
        }

        fun submittedDateTimeIsNull(): Specification<MarketSaleDocument> {
            return Specification { root,
                                   _,
                                   criteriaBuilder ->
                criteriaBuilder.isNull(root.get<OffsetDateTime>("submittedDateTime"))
            }
        }

        fun accountIn(accountIds: List<Int>):Specification<MarketSaleDocument>{
            return SpecificationUtils.`in`({ root ->
                root.joinOnce<MarketSaleDocument,MarketAccount>("account").get("id")
            }, accountIds)
        }
    }


}

